package com.test.io.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;

import javax.print.attribute.standard.Severity;

import com.test.io.common.CommonUtil;


public class MultiplexerTimeServer implements Runnable {

	//多路复用选择器
	private Selector selector;
	
	private ServerSocketChannel serverChannel;
	private volatile boolean stop;
	
	public MultiplexerTimeServer(int port){
		try {
			this.selector=Selector.open();
			this.serverChannel=ServerSocketChannel.open();
			this.serverChannel.configureBlocking(false);
			this.serverChannel.socket().bind(new InetSocketAddress(port),1024);
			this.serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);
			System.out.println("the time server is started in port :"+port);
			
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
	
	public void stop(){
		this.stop=true;
	}
	
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(!stop){
			try {
				//阻塞方法  直到有至少一个通道准备就绪  返回多少通道准备就绪
				int ct=this.selector.select();//每个1s唤醒一次
				System.out.println(CommonUtil.getCurrentTime()+"=keys in ready is:"+ct);
//				this.selector.selectNow(); //不阻塞不管什么通道准备就绪 都立即返回
				Set<SelectionKey> selectedKeys=this.selector.selectedKeys();
				Iterator<SelectionKey> it=selectedKeys.iterator();
				SelectionKey key=null;
				while(it.hasNext()){
					key=it.next();
					it.remove();
					try {
						this.handleInput(key);
					} catch (Exception e) {
						// TODO: handle exception
						if(key!=null){
							key.cancel();
							if(key.channel()!=null){
								key.channel().close();
							}
						}
						e.printStackTrace();
						
					}
					
				}
				
			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}
		
		if(this.selector!=null){
			try {
				this.selector.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		
		
	}
	
	private void handleInput(SelectionKey key)throws IOException{
		System.out.println("handleInput=>"+key.hashCode()+"--isValid:"+key.isValid()
				+"--acceptable:"+key.isAcceptable()+"--readable:"+key.isReadable());
		if(key.isValid()){
			//此键的通道是否已准备好接受新的的套接字链接
			if(key.isAcceptable()){
				System.out.println("key is acceptable..");
				//返回为之创建此键的通道
				ServerSocketChannel ssc=(ServerSocketChannel)key.channel();
				// 接受到此通道套接字的连接。
	            // 此方法返回的套接字通道（如果有）将处于阻塞模式。
				SocketChannel sc=ssc.accept();
				// 配置为非阻塞
				sc.configureBlocking(false);
				// 注册到selector，等待连接
				sc.register(this.selector, SelectionKey.OP_READ);
				System.out.println("key is resiter to read");
			}
			//次键的通道已经准备好读取操作
			if(key.isReadable()){
				System.out.println("key is readable");
				SocketChannel sc=(SocketChannel)key.channel();
				ByteBuffer bufferRead=ByteBuffer.allocate(1024);
				int readBytes=sc.read(bufferRead);
				System.out.println("had read length :"+readBytes);
				if(readBytes>0){
					//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
					bufferRead.flip();
					//返回剩余可用长度
					byte[] bytes=new byte[bufferRead.remaining()];
					bufferRead.get(bytes);
					String body=new String(bytes, "UTF-8");
					System.out.println("the time server receive order is :"+body);
					String order="";
					String currentTime=order.equalsIgnoreCase(body)||body.endsWith("")
							?new Date().toString():"bad order ";
					this.doWrite(sc, currentTime);
				}else if(readBytes<0){
					key.cancel();
					sc.close();
				}else{
					
				}
			}
		}
	}
	
	
	public void doWrite(SocketChannel sc,String response)throws IOException{
		if(response==null|| response.length()==0){
			System.out.println("response is null");
			return;
		}
		System.out.println("ready write back to client");
		byte[] bytes=response.getBytes();
		ByteBuffer writeBuffer= ByteBuffer.allocate(bytes.length);
		writeBuffer.put(bytes);
		//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
		writeBuffer.flip();
		System.out.println("start to write");
		sc.write(writeBuffer);
		System.out.println("finish to write");
	}
	

}
